//*************************************************************************************
/** \file task_battery.cc
 *    This file contains a class which battery charge controller. The charge controller
 *    is used to maintain the energy level in a lead-acid battery using a solar panel
 *    with a shunt transistor to deactivate solar charging when the battery is full.
 *    The battery voltage is monitored and can be transmitted with a radio modem to
 *    allow the system's condition to be monitored remotely. 
 *
 *  Revisions:
 *    \li 12-26-2007 JRR Original file
 *    \li 12-30-2007 JRR Corrected spelling of Fahrenheit
 *    \li 06-01-2008 JRR Changed serial stuff to use base_text_serial and descendents
 *    \li 09-19-2009 JRR Battery charge control task adapted from temperature task
 */
//*************************************************************************************


#include <stdlib.h>
#include <avr/io.h>
#include "base_text_serial.h"				// For writing things to serial devices
#include "global_debug.h"					// Debugging from all parts of the program
#include "stl_timer.h"						// Timer measures real time
#include "task_battery.h"					// Header for this task


//-------------------------------------------------------------------------------------
/** This constructor creates a new battery charge control task object. The user must 
 *  have previously created an object of class adc_8bit which runs the A/D converter.
 *  @param p_adc A pointer to the A/D converter driver object
 *  @param adc_channel The A/D channel used to measure battery voltage
 *  @param control_port The port used for the shunt control bit, such as PORTD
 *  @param control_ddr The data direction register which controls the shunt control port
 *  @param shunt_bit The number (0 - 7) of the bit used to control the shunt transistor
 *  @param a_timer A reference to the task timer
 *  @param interval A time stamp containing the time interval between runs
 */

task_battery::task_battery (adc_10bit* p_adc, uint8_t adc_channel, 
		volatile uint8_t& control_port, volatile uint8_t& control_ddr, 
		uint8_t shunt_bit, task_timer& a_timer, time_stamp interval)
	: ctrl_port (control_port), ctrl_ddr (control_ddr), stl_task (a_timer, interval)
{
	// Save the channel used to measure battery voltage
	adc_chan = adc_channel;

	// Save a pointer to the A/D converter driver
	p_the_adc = p_adc;

	// Compute a bitmask for the shunt resistor's control line
	ctrl_mask = 1 << shunt_bit;

	// Use the data direction register to set the control port bit to be output
	ctrl_ddr |= ctrl_mask;

	// Turn the shunt transistor off to begin with
	ctrl_port &= ~ctrl_mask;

	// We haven't measured anything, so let the voltage be zero
	most_recent_voltage = 0;

	// Set the full-battery and empty-battery counters to 0
	full_counter = 0;
	empty_counter = 0;

	GLOB_DEBUG ("Battery charge controller task constructor" << endl);
}


//-------------------------------------------------------------------------------------
/** This is the function which runs when it is called by the task scheduler. It gets a
 *  temperature reading from the DS1822 sensor, then saves that reading and adds it to
 *  the averaging sum so that an average temperature can be computed later.
 *  @param state The state of the task when this run method begins running
 *  @return The state to which the task will transition, or STL_NO_TRANSITION if no
 *      transition is called for at this time
 */

char task_battery::run (char state)
{
// 	GLOB_DEBUG ("Raw 4: " << p_the_adc->read_n_times (BATT_OVERSAMPLE, 4) 
// 		<< " 5: " << p_the_adc->read_n_times (BATT_OVERSAMPLE, 5)
// 		<< " 6: " << p_the_adc->read_n_times (BATT_OVERSAMPLE, 6) 
// 		<< " 7: " << p_the_adc->read_n_times (BATT_OVERSAMPLE, 7) << endl);

	// Read the battery voltage 
	int raw_reading = p_the_adc->read_n_times (adc_chan, BATT_OVERSAMPLE);

	// Compute the voltage from the raw A/D reading
	most_recent_voltage = ((uint16_t)raw_reading * BATT_NUMER) / BATT_DENOM;

	switch (state)
	{
		// State 0 is charging; if the battery voltage isn't over the maximum charging
		// threshold, keep charging
		case (0):
			// The shunt transistor must be turned off in this state
			ctrl_port &= ~ctrl_mask;

			// Check if the battery voltage is over the maximum.  If so, count the
			// number of times over until reaching the number which tells us it's
			// time to turn on the shunt and stop charging for a while
			if (most_recent_voltage > BATT_MAX_A2D)
			{
				// If the battery has been full for a long time
				if (++full_counter > BATT_FULL_MAX_CT)
				{
					full_counter = 0;
					return (1);
				}
				else
				{
					return (STL_NO_TRANSITION);
				}
			}
			else
			{
				return (STL_NO_TRANSITION);
			}

		// In state 1, the battery has been fully charged so the panel is in shunt mode
		// until the voltage drops to the start-charging-again threshold
		case (1):
			// Turn the shunt resistor on in this state
			ctrl_port |= ctrl_mask;

			// Check if the battery voltage has fallen below the minimum
			if (most_recent_voltage < BATT_MIN_A2D)
			{
				// If the battery has been empty for a long time
				if (++empty_counter > BATT_EMPTY_MAX_CT)
				{
					empty_counter = 0;
					return (0);
				}
				else
				{
					return (STL_NO_TRANSITION);
				}
			}
			else
				return (STL_NO_TRANSITION);

		default:
			GLOB_DEBUG ("ERROR: Battery task in state " << state << endl);
			return (0);
	}
}


//-------------------------------------------------------------------------------------
/** This method returns the most recently computed average voltage.
 */

uint16_t task_battery::get_voltage (void)
{
	return (most_recent_voltage);
}


//-------------------------------------------------------------------------------------
/** This overloaded operator allows a battery task object to send the measured voltage
 *  as a text string to a serial device.
 *  @param ser_dev A reference to the serial device to which the voltage is printed
 *  @param batt_task A reference to the battery task whose voltage is to be printed
 *  @return A reference to the serial device to which things are being sent
 */

base_text_serial& operator<< (base_text_serial& ser_dev, task_battery& batt_task)
{
	ser_dev << (batt_task.get_voltage () / 100) << "." 
		<< (batt_task.get_voltage () / 10) % 10 
		<< batt_task.get_voltage () % 10;
}
